﻿#define _CRT_SECURE_NO_WARNINGS 1
#include"Blog_Snack.h"

//隐藏光标
void Hide_cursor(void)
{
	//获得输出控制台句柄
	HANDLE houtput = NULL;
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	//获得控制台光标信息
	CONSOLE_CURSOR_INFO cursor_info;
	GetConsoleCursorInfo(houtput, &cursor_info);
	//将光标信息中的可见度改为0
	cursor_info.bVisible = 0;
	SetConsoleCursorInfo(houtput, &cursor_info);
}

//定位光标位置
void SetPos(size_t x, size_t y)
{
	//获得输出控制台句柄
	HANDLE houtput = NULL;
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	// 获取光标
	COORD cursorPos = { 0, 0 };
	
	//修改光标位置
	cursorPos.X = x;
	cursorPos.Y = y;
	SetConsoleCursorPosition(houtput,cursorPos);
}

#include<locale.h>
void Prepare(void)
{
	//先将程序改成本地模式
	setlocale(LC_ALL, "");
	//先对控制台的大小进行修改
	system("mode con cols=150 lines=40");

	//对名称进行重命名
	system("title 贪吃蛇");
}


//加载界面的打印
void ProcessBar(void)
{
	wchar_t bar[102] = { '\0' }; //设置一个字符串来当作进度条
	int cnt = 0;
	while (cnt <= 100)
	{
		SetPos(25, 21);  //定位
		wprintf(GREEN L"[%-101ls]" RESET"[%d%%]", bar, cnt); //打印进度条，并加上颜色
		Sleep(1);      //休眠100毫秒
		bar[cnt] = L'█';
		cnt++;
	}
}

void Print_Page1(void)
{
	//定位光标，打印欢迎语句
	SetPos(65, 18);
	wprintf(YELLOW L"欢迎进入贪吃蛇游戏" RESET);
	//打印加载的进度条
	ProcessBar();
}

void Print_Page2(void)
{
	system("cls");//清屏
	SetPos(58, 17);
	wprintf(MAGENTA L"用↑↓←→来控制方向,ESC退出，SPACE暂停；");
	SetPos(58, 18);
	wprintf(L"F1加速，F2减速；");
	SetPos(58, 19);
	wprintf(L"加速可以获得更多积分，减速获得积分变慢；" RESET);
	
	SetPos(58, 21);
	system("pause");
}



//打印墙体
void Print_Wall(void)
{
	SetPos(0, 0);
	//打印横向
	for (int i = 0; i < LEN; i += 2)  //注意此处是+2，因为宽字符的宽度是2不像普通字符一样
		wprintf(L"%c", WALL);
	SetPos(0, WIDTH);
	for (int i = 0; i < LEN; i += 2)  
		wprintf(L"%c", WALL);

	//打印纵向
	for (int i = 1; i < WIDTH; i++)
	{
		SetPos(0, i);
		wprintf(L"%c", WALL);
		SetPos(LEN-2, i);
		wprintf(L"%c", WALL);
	}
}

//打印说明
void Print_RULE(void)
{
	SetPos(100, 8);
	wprintf(GREEN L"↑↓←→来控制方向");
	SetPos(100, 9);
	wprintf(L"ESC退出，SPACE暂停");
	SetPos(100, 10);
	wprintf(L"F1加速，F2减速");
	SetPos(100, 11);
	wprintf(L"加速可以获得更多积分，减速获得积分变慢" RESET);
}

//创造节点
Snackbody* MakeBody()
{
	Snackbody* newbody = (Snackbody*)malloc(sizeof(Snackbody));
	newbody->_next = NULL;
	newbody->_x = 0;
	newbody->_y = 0;
	return newbody;
}

//对蛇进行初始化
void Init_Snack(Snack* sn)
{
	//对蛇的各个数据进行初始化
	sn->_head = NULL;
	sn->dir = RIGHT;
	sn->state = FINE;
	sn->_score = 0;
	sn->_speed = 200;

	//创建蛇身
	Snackbody** snhead = &(sn->_head);  //注意此处需要是二级指针，因为要对其地址进行修改
	Snackbody* tail = *snhead;
	for (int i = 0; i < 5; i++)
	{
		Snackbody* newbody = MakeBody();
		newbody->_x = 20 - i * 2;
		newbody->_y = 15;
		if ((*snhead) == NULL)
		{
			*snhead = newbody;
			tail = newbody;
		}
		else
		{
			(tail)->_next = newbody;
			tail = (tail)->_next;
		}
	}
	//打印
	Snackbody* cur = sn->_head;
	while (cur != NULL)
	{
		SetPos(cur->_x, cur->_y);
		wprintf(L"%c", SNACK);
		cur = cur->_next;
	}
}

//检查食物
bool CHECK_food(Snack* sn)
{
	size_t x = sn->pf->x;
	size_t y = sn->pf->y;
	Snackbody* cur = sn->_head;
	while (cur != NULL)
	{
		if (cur->_x == x && cur->_y == y)
			return false;
		cur = cur->_next;
	}
	return true;
}

//创造食物
void Make_food(Snack* sn)
{
	sn->pf = (Food*)malloc(sizeof(Food));
	sn->pf->_each_score = 10;

	srand((unsigned int)time(NULL));
again:
	sn->pf->x = rand()%65+2;   //食物x范围是2-66
	sn->pf->y = rand()%21+1;   //y的范围是1-21
	//检查食物是否符合要求
	//x必须是偶数，食物不能出现在蛇身上
	if (sn->pf->x % 2 != 0 || !CHECK_food(sn))
	{
		goto again;
	}
	SetPos(sn->pf->x, sn->pf->y);
	wprintf(L"%c", FOOD);
}

//下一个位置是食物
void IS_FOOD(Snack* sn,Snackbody* pnext)
{
	//将食物直接当成蛇头
	pnext->_next = sn->_head;
	sn->_head = pnext;
	SetPos(pnext->_x, pnext->_y);
	wprintf(L"%c", SNACK);
	//加分
	sn->_score += sn->pf->_each_score;
	//创建食物
	Make_food(sn);
}

//下一个位置不是食物
void NO_FOOD(Snack* sn, Snackbody* pnext)
{
	//将下一个节点当成蛇头
	pnext->_next = sn->_head;
	sn->_head = pnext;
	//找到最后一个节点并释放
	Snackbody* cur = sn->_head;
	while (cur->_next->_next != NULL)
		cur = cur->_next;
	//将蛇尾从原本的蛇改为空
	SetPos(cur->_next->_x, cur->_next->_y);
	wprintf(L"  ");
	free(cur->_next);
	cur->_next = NULL;
	//将蛇的下一个位置打印成蛇
	SetPos(pnext->_x, pnext->_y);
	wprintf(L"%c", SNACK);
}

//打印下一张图片
void Print_NEXT(Snack* sn)
{
	//创建下一个位置的节点
	Snackbody* pnext = (Snackbody*)malloc(sizeof(Snackbody));
	switch(sn->dir)
	{
	case UP:
		pnext->_x = sn->_head->_x;
		pnext->_y = sn->_head->_y - 1;
		break;
	case DOWN:
		pnext->_x = sn->_head->_x;
		pnext->_y = sn->_head->_y + 1;
		break;
	case LEFT:
		pnext->_x = sn->_head->_x - 2;
		pnext->_y = sn->_head->_y;
		break;
	case RIGHT:
		pnext->_x = sn->_head->_x + 2;
		pnext->_y = sn->_head->_y;
		break;
	}
	//下一个位置是食物
	if (pnext->_x == sn->pf->x && pnext->_y == sn->pf->y)
	{
		IS_FOOD(sn,pnext);
	}
	else
		NO_FOOD(sn,pnext);
}

//暂停
void SPACE()
{
	while (1)
	{
		if (KEY_PRESS(VK_SPACE))
			break;
	}
}

//检查是不是墙
void IF_WALL(Snack* sn)
{
	if (sn->_head->_x >= LEN - 2 || sn->_head->_x == 0)
		sn->state = DEAD_BYWALL;
	if (sn->_head->_y == WIDTH || sn->_head->_y == 0)
		sn->state = DEAD_BYWALL;
}

//检查是否撞到自己
void IF_SELF(Snack* sn)
{
	int x = sn->_head->_x;
	int y = sn->_head->_y;

	Snackbody* cur = sn->_head->_next;
	while (cur != NULL)
	{
		if (cur->_x == x && cur->_y == y)
			sn->state = DEAD_BYBODY;
		cur = cur->_next;
	}
}

//游戏结束
void Game_over(Snack* sn)
{
	system("cls");
	SetPos(65, 18);
	switch (sn->state)
	{
	case DEAD_BYWALL:
		wprintf(MAGENTA L"你撞到墙了！！！");
		break;
	case DEAD_BYBODY:
		wprintf(L"你撞到自己了！！！");
		break; 
	case QUIT:
		wprintf(L"已退出！！！" RESET);
		break;
	}
	SetPos(0, 35);
	//释放蛇身各个节点，释放动态开辟的空间
	Snackbody* cur = sn->_head;
	while (cur != NULL)
	{
		Snackbody* next = cur->_next;
		free(cur);
		cur = next;
	}
	free(sn->pf);
}


//游戏运行
void start(Snack* sn)
{
	do {
		SetPos(100, 6);
		wprintf(L"总得分：%d", sn->_score);
		SetPos(100, 7);
		wprintf(L"当前食物分数：%2d", sn->pf->_each_score);
		//检查按键
		if (KEY_PRESS(VK_UP) && sn->dir != DOWN)
			sn->dir = UP;
		if (KEY_PRESS(VK_DOWN) && sn->dir != UP)
			sn->dir = DOWN;
		if (KEY_PRESS(VK_LEFT) && sn->dir != RIGHT)
			sn->dir = LEFT;
		if (KEY_PRESS(VK_RIGHT) && sn->dir != LEFT)
			sn->dir = RIGHT;
		if (KEY_PRESS(VK_F1))
		{
			if (sn->_speed > 40)
			{
				sn->pf->_each_score += 2;
				sn->_speed -= 40;
			}
		}
		if (KEY_PRESS(VK_F2))
		{
			if (sn->pf->_each_score > 2)
			{
				sn->pf->_each_score -= 2;
				sn->_speed += 40;
			}
		}
		if (KEY_PRESS(VK_SPACE))
			SPACE();
		if (KEY_PRESS(VK_ESCAPE))
		{
			sn->state = QUIT;
			break;
		}
		Sleep(sn->_speed);   //休眠，准备打印下一张图片
		Print_NEXT(sn);
		IF_WALL(sn);
		IF_SELF(sn);
	} while (sn->state == FINE);

	//游戏结束
	Game_over(sn);

}


